package com.fantasy.easy.sys.i18n.resource;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.support.AbstractMessageSource;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fantasy.easy.core.Const;
import com.fantasy.easy.core.entity.SysI18nEntity;
import com.fantasy.easy.sys.i18n.mapper.SysI18nMapper;

@Component("messageSource")
public class I18nMessageResource extends AbstractMessageSource implements ResourceLoaderAware {

    @Autowired
    private SysI18nMapper i18nService;

    private final Logger log = LoggerFactory.getLogger(getClass());

    //cache resource
    private static final Map<String, Map<String, String>> LOCAL_CACHE = new ConcurrentHashMap<>(256);

    //如果想在生成对象时候完成某些初始化操作，而偏偏这些初始化操作又依赖于依赖注入，那么就无法在构造函数中实现。
    //为此，可以使用@PostConstruct注解一个方法来完成初始化，@PostConstruct注解的方法将会在依赖注入完成后被自动调用
    @PostConstruct
    public void init() {
        this.reload();
    }

    public void reload() {
    	log.debug("国际化重新加载");
        LOCAL_CACHE.clear();//clear cache while reload data.
        LOCAL_CACHE.putAll(getAllMessageResource());
    }

    public Map<String, Map<String, String>> getAllMessageResource() {
    	log.debug("获取所有的国际化");
		List<SysI18nEntity> messageI18NPropertyList = i18nService.selectList(new QueryWrapper<>());
        if (CollectionUtils.isEmpty(messageI18NPropertyList)) {
            return new HashMap<String, Map<String, String>>();
        }
        final Map<String, Map<String, String>> messageResources = new HashMap<>();
        final Map<String, String> zhCnMessageResources = new HashMap<>(messageI18NPropertyList.size());
        final Map<String, String> enUsMessageResources = new HashMap<>(messageI18NPropertyList.size());

        for (SysI18nEntity message : messageI18NPropertyList) {
            String locale = message.getI18nLang();
            if (Const.LANG_EN.equals(locale)) {
                enUsMessageResources.put(message.getI18nKey(), message.getI18nContent());
            } else if (Const.LANG_CN.equals(locale)) {
                zhCnMessageResources.put(message.getI18nKey(), message.getI18nContent());
            }
        }

        //put into local cache
        LOCAL_CACHE.put(Locale.ENGLISH.getLanguage(), enUsMessageResources);
        LOCAL_CACHE.put(Locale.CHINESE.getLanguage(), zhCnMessageResources);

        return messageResources;
    }

    private String checkFromCachedOrBundResource(String code, Locale locale) {
        String language = locale.getLanguage();
        Map<String, String> props = LOCAL_CACHE.get(language);
        if (null != props && props.containsKey(code)) {
            return props.get(code);
        } else {
            //check from parent message resource. and catch no such element exception.
            try {
                if (null != this.getParentMessageSource()) {
                    return this.getParentMessageSource().getMessage(code, null, locale);
                }
            } catch (Exception ex) {
                logger.error(ex.getMessage(), ex);
            }
            return code;
        }
    }

    @Override
    protected MessageFormat resolveCode(String code, Locale locale) {
        //first check from local cache, is none exists. then query from parent message resource which load from bundle resrouces
        String msg = checkFromCachedOrBundResource(code, locale);
        MessageFormat messageFormat = new MessageFormat(msg, locale);
        return messageFormat;
    }

    @Override
    protected String resolveCodeWithoutArguments(String code, Locale locale) {
        return checkFromCachedOrBundResource(code, locale);
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        resourceLoader = (resourceLoader == null ? new DefaultResourceLoader() : resourceLoader);
    }
}